/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_kernel.htm
 *
 * Zhiqim Kernel is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.kernel.logging;

import java.io.File;

import org.zhiqim.kernel.Z;
import org.zhiqim.kernel.config.Group;
import org.zhiqim.kernel.schedule.Task;
import org.zhiqim.kernel.schedule.TaskThreader;
import org.zhiqim.kernel.service.Servicer;
import org.zhiqim.kernel.schedule.ScheduleFactory;
import org.zhiqim.kernel.schedule.Scheduler;
import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Linux;
import org.zhiqim.kernel.util.Systems;
import org.zhiqim.kernel.util.Validates;

/**
 * 日志切割器，用于如[nohup|nginx]等生成的日志按天等条件切割，而不影响日志打印
 * <group id="logcutter" desc="日志切割器">
 *     <item key="filePath" value="./nohup.out" update="false" desc="日志文件地址"/>
 *     <item key="fileBakDir" value="./bak" update="false" desc="日志文件备份目录，为空表示和日志文件同目录"/>
 *     <item key="schedule" value="ZDay:0,0,0:false" update="false" desc="时刻安排"/>
 *     <item key="maxNum" value="7" update="false" desc="最大备份文件个数"/>
 *     <item key="keepLogNum" value="30" update="false" desc="保留日志条数在原日志文件"/>
 * </group>
 *
 * @version v1.0.0 @author zouzhigang 2015-12-31 新建与整理
 */
public class LogLinuxCutter extends Servicer implements Task
{
    private static final Log log = LogFactory.getLog(LogLinuxCutter.class);
    
    //任务参数
    protected Scheduler scheduler;
    protected boolean schedulerCreated;
    protected int taskId;
    
    //配置参数
    protected String filePath;
    protected String fileDir;
    protected String fileName;
    protected String fileBakDir;
    protected String schedule;
    protected int maxNum;
    protected int keepLogNum;
    
    @Override
    public boolean create() throws Exception
    {
        if (!Systems.isLinux())
        {
            log.info("日志切割器[%s]系统不是linux，不处理", id);
            return true;
        }
        
        Group group = Z.conf().group(id);
        if (group == null)
        {
            log.error("日志切割器[%s]没有配置项", id);
            return false;
        }
        
        //日志文件路径
        filePath = group.getString("filePath");
        if (filePath == null)
        {
            log.error("日志切割器[%s.filePath]配置不正确", id);
            return false;
        }
        
        File file = new File(filePath);
        if (!Files.isFile(file) || !file.canRead())
        {
            log.error("日志切割器[%s.filePath]文件不存在，或不能读", id);
            return false;
        }
        
        fileName = file.getName();
        fileDir = Files.toLinuxPath(file.getParentFile().getCanonicalPath());
        
        //日志文件备份目录
        fileBakDir = group.getString("fileBakDir");
        if (Validates.isEmptyBlank(fileBakDir))
            fileBakDir = fileDir;
        else
        {
            Files.mkDirectory(fileBakDir);
            File fileDir = new File(fileBakDir);
            fileBakDir = Files.toLinuxPath(fileDir.getCanonicalPath());
        }
        
        //时刻安排
        schedule = group.getString("schedule", "{name:Day, param:0/0/0, first:false}");
        TaskThreader threader = ScheduleFactory.getTaskThreader(this, schedule);
        if (threader == null)
        {
            log.error("日志切割器[%s.schedule]配置不正确", id);
            return false;
        }
        
        //最大备份文件个数，值在0-90之间(3个月以内)
        maxNum = group.getInt("maxNum", 0);
        if (maxNum < 0)
            maxNum = 0;
        
        if (maxNum > 90)
            maxNum = 90;
        
        //保留日志条件在原文件中，值在1-300之间(Window的CMD默认300)
        keepLogNum = group.getInt("keepLogNum", 1);
        if (keepLogNum < 1)
            keepLogNum = 1;
        
        if (keepLogNum > 300)
            keepLogNum = 300;
        
        //建立任务
        scheduler = Z.serv().get(Scheduler.class);
        if (scheduler == null)
        {
            scheduler = Z.glb().getc(Scheduler.class);
            scheduler.create();
            schedulerCreated = true;
        }
        
        taskId = scheduler.addTask(threader);
        log.info("日志切割器[%s][%s]初始化完成!!!", id, filePath);
        return true;
    }

    @Override
    public void destroy() throws Exception
    {
        if (scheduler != null && taskId != 0)
            scheduler.removeTask(taskId);
        
        if (schedulerCreated && scheduler.size() == 0 && scheduler.sizeInterval() == 0)
        {//自己创建的且没有任务了
            scheduler.destroy();
            scheduler = null;
        }
    }
    
    @Override
    public void execute()
    {
        try
        {
            log.info("日志切割器[%s]切割[%s]开始...", id, filePath);
            
            //删除和重命名备份目录下的文件
            Files.removeMaxFileAndRenameFile(fileBakDir, fileName, maxNum, false);
            
            //1.先备份${filePath}到${fileBakDir}/${fileName}.1
            //2.再备份${filePath}最后${keepLogNum}条数据到${filePath}.tmp
            //3.再覆盖${filePath}.tmp数据到${filePath}
            //4.最后把${filePath}.tmp删除
            StringBuilder strb = new StringBuilder();
            if (maxNum > 0){
                strb.append("cat ").append(filePath).append(" > ").append(fileBakDir).append("/").append(fileName).append(".1;");
            }
            strb.append("tail -").append(keepLogNum).append(" ").append(filePath).append(" > ").append(filePath).append(".tmp;");
            strb.append("cat ").append(filePath).append(".tmp > ").append(filePath).append(";");
            strb.append("rm -rf ").append(filePath).append(".tmp;");
            
            Linux.shellNotResult(strb.toString());
        }
        catch (Exception e)
        {
            log.error("日志切割器[%s]切割[%s]失败", e, id, filePath);
        }
        
        log.info("日志切割器[%s]切割[%s]完成!!!", id, filePath);
    }
}
